package com.qysoft.rapid.dao.mybatis;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.session.SqlSession;

import com.jfinal.log.Logger;
import com.qysoft.rapid.consts.RapidConsts;
import com.qysoft.rapid.domain.Bean;
import com.qysoft.rapid.domain.BeanBuilder;
import com.qysoft.rapid.exceptions.RapidException;
import com.qysoft.rapid.plugin.mybatis.MyBatisPlugin;
import com.qysoft.rapid.plugin.mybatis.MyBatisSessionManager;

/**
 * rapid平台通用数据库访问工具
 * 我不生产代码，我只是代码的搬运工
 * @author liugong
 *
 */
public class RapidDao extends MyBatisDao{
	
	//获取sqlsession
	private static SqlSession getSqlSession(){
		return MyBatisPlugin.getSqlSessionFactory().openSession(true);
	}

	//默认主键
	private static final String defaultPrimaryKey = "id";
	
	//log
	private static final Logger log = Logger.getLogger(RapidDao.class);
	
	/**
	 * 通用单表保存(默认主键为"id")
	 * @param tableName 数据库表名
	 * @param bean 实体bean
	 * @return
	 * @throws Exception 
	 */
	public static boolean save(String tableName,Bean bean) throws Exception{
		return save(tableName, defaultPrimaryKey,bean);
	} 

	/**
	 * 通用单表保存
	 * @param tableName	数据库表名
	 * @param primaryKey 主键（复合主键以逗号分隔）
	 * @param bean 实体bean
	 * @return
	 * @throws SQLException
	 */
	public static boolean save(String tableName, String primaryKey, Bean bean) throws Exception {
		boolean isRapidConn = false; //是否存在连接注入
		SqlSession session = MyBatisSessionManager.getSession();
		if (session!=null) {
			isRapidConn = true;
		}else {
			session = getSqlSession();
		}
		try {
			String[] pKeys = primaryKey.split(",");
			List<Object> paras = new ArrayList<Object>();
			StringBuilder sql = new StringBuilder();
			SqlBuilder.forBeanSave(sql, paras, tableName, pKeys, bean);
			PreparedStatement pst;
			pst = session.getConnection().prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
			SqlBuilder.fillStatement(pst, paras);
			doSqlLog(sql.toString());
			doParLog(paras);
			int result = pst.executeUpdate();
			getGeneratedKey(pst, bean, pKeys);
			closeStatement(pst); 
			return result >= 1;
		} catch (Exception e) {
			throw e;
		}finally{
			if (!isRapidConn&&session!=null) {
				session.close();
			}
		}
	}	
	
	/**
	 * 通用单表删除(默认为主键id为删除条件)
	 * @param tableName	表名
	 * @param idValue id值
	 * @return
	 * @throws Exception
	 */
	public static boolean deleteById(String tableName,Object idValue) throws Exception{
		return deleteById(tableName, defaultPrimaryKey, idValue);
	}
	
	/**
	 * 通用单表删除
	 * @param tableName 表名
	 * @param primaryKey 主键（复合主键以逗号分隔）
	 * @param idValue id值，多参数
	 * @return
	 * @throws Exception
	 */
	public static boolean deleteById(String tableName, String primaryKey, Object... idValue) throws Exception {
		String[] pKeys = primaryKey.split(",");
		if (pKeys.length != idValue.length)
			throw new RapidException("primary key number must equals id value number");
		String sql = SqlBuilder.forBeanDeleteById(tableName, pKeys);
		return executeSql(sql, idValue) >= 1;
	}
	
	/**
	 * 通用单表更新操作（默认主键为id）
	 * @param tableName 表名
	 * @param bean 实体bean
	 * @return
	 * @throws Exception
	 */
	public static boolean update(String tableName,Bean bean) throws Exception{
		return update(tableName, defaultPrimaryKey,bean);
	}

	
	/**
	 * 通用单表更新操作（默认主键为id）
	 * @param tableName 表名
	 * @param bean 实体bean
	 * @return
	 * @throws Exception
	 */
	public static boolean updateColumn(String tableName,Bean bean) throws Exception{
		return updateColumn(tableName, defaultPrimaryKey,bean);
	}
	/**
	 * 通用单表更新操作
	 * @param tableName 表名
	 * @param primaryKey 主键（复合主键以，分隔）
	 * @param bean 实体bean
	 * @return
	 * @throws Exception
	 */
	public static boolean update(String tableName, String primaryKey, Bean bean) throws Exception { 
		String[] pKeys = primaryKey.split(",");
		Object[] ids = new Object[pKeys.length];
		for (int i=0; i<pKeys.length; i++) {
			ids[i] = bean.get(pKeys[i].trim());	// .trim() is important!
			if (ids[i] == null)
				throw new RapidException("You can't update record without Primary Key, " + pKeys[i] + " can not be null.");
		}
		StringBuilder sql = new StringBuilder();
		List<Object> paras = new ArrayList<Object>();
		SqlBuilder.forBeanUpdate(tableName, pKeys, ids, bean, sql, paras);
		if (paras.size() <= 1) {
			return false;
		}
		return executeSql(sql.toString(), paras.toArray()) >= 1;
	}	

	/**
	 * 通用表更新（只更新修改后的列）
	 * @param tableName 表名
	 * @param primaryKey 主键 复合主键以，分隔
	 * @param bean 实体bean
	 * @return
	 * @throws Exception
	 */
	public static boolean updateColumn(String tableName, String primaryKey, Bean bean) throws Exception { 
		String[] pKeys = primaryKey.split(",");
		Object[] ids = new Object[pKeys.length];
		for (int i=0; i<pKeys.length; i++) {
			ids[i] = bean.get(pKeys[i].trim());	// .trim() is important!
			if (ids[i] == null)
				throw new RapidException("You can't update record without Primary Key, " + pKeys[i] + " can not be null.");
		}
		StringBuilder sql = new StringBuilder();
		List<Object> paras = new ArrayList<Object>();
		SqlBuilder.forBeanUpdate(tableName, pKeys, ids,bean, sql, paras);
		if (paras.size() <= 1) {
			return false;
		}
		return executeSql(sql.toString(), paras.toArray()) >= 1;
	}		
	
	/**
	 * 执行sql
	 * @param sql
	 * @param paras
	 * @return
	 * @throws Exception
	 */
	public static int executeSql(String sql, Object... paras) throws Exception {
		boolean isRapidConn = false; //是否存在连接注入
		SqlSession session = MyBatisSessionManager.getSession();
		if (session!=null) {
			isRapidConn = true;
		}else {
			session = getSqlSession();
		}
		try {
			PreparedStatement pst = session.getConnection().prepareStatement(sql);
			SqlBuilder.fillStatement(pst, paras);
			doSqlLog(sql);
			doParLog(paras);
			int result = pst.executeUpdate();
			closeStatement(pst); 
			return result;
		} catch (Exception e) {
			throw e;
		}finally{
			if (!isRapidConn&&session!=null) {
				session.close();
			}
		}
	}
	
	/**
	 * 公用查询（默认主键为"id"）
	 * @param tableName 表名
	 * @param idValue id值
	 * @return
	 * @throws Exception
	 */
	public static Bean findById(String tableName,Object idValue) throws Exception{
		return findById(tableName, defaultPrimaryKey, idValue);
	}
	
	/**
	 * 公用查询
	 * @param tableName 表名
	 * @param primaryKey 主键（复合主键以，分隔）
	 * @param idValue id值
	 * @return
	 * @throws Exception
	 */
	public static Bean findById(String tableName, String primaryKey, Object... idValue) throws Exception {
		String[] pKeys = primaryKey.split(",");
		if (pKeys.length != idValue.length)
			throw new RapidException("primary key number must equals id value number");
		
		String sql = SqlBuilder.forBeanFindById(tableName, pKeys);
		List<Bean> result = find(sql, idValue);
		return result.size() > 0 ? result.get(0) : null;
	}	
	
	/**
	 * 公用查询，指定返回的列
	 * @param tableName 表名
	 * @param columns 返回的列名以，分隔
	 * @param primaryKey 主键，复合主键以，分隔
	 * @param idValue id值
	 * @return
	 * @throws Exception
	 */
	public static Bean findColumnById(String tableName,String columns,String primaryKey,Object... idValue) throws Exception {
		String[] pKeys = primaryKey.split(",");
		if (pKeys.length != idValue.length)
			throw new RapidException("id values error, need " + pKeys.length + " id value");
		String sql = SqlBuilder.forBeanFindById(tableName, columns,pKeys);
		List<Bean> result = find(sql, idValue);
		return result.size() > 0 ? result.get(0) : null;
	}	
	
	//查询
	public static List<Bean> find(String sql, Object... paras) throws Exception {
		boolean isRapidConn = false; //是否存在连接注入
		SqlSession session = MyBatisSessionManager.getSession();
		if (session!=null) {
			isRapidConn = true;
		}else {
			session = getSqlSession();
		}
		try {
			PreparedStatement pst = session.getConnection().prepareStatement(sql);
			SqlBuilder.fillStatement(pst, paras);
			doSqlLog(sql);
			doParLog(paras);
			ResultSet rs = pst.executeQuery();
			List<Bean> result = BeanBuilder.build(rs);
			closeRsAndStatement(rs, pst);
			return result;
		} catch (Exception e) {
			throw e;
		}finally{
			if (!isRapidConn&&session!=null) {
				session.close();
			}
		}
	}	
	
	/**
	 * 查询单条数据
	 * @param sql
	 * @param paras
	 * @return
	 * @throws Exception
	 */
	public static Object queryOne(String sql, Object... paras) throws Exception {
		boolean isRapidConn = false; //是否存在连接注入
		SqlSession session = MyBatisSessionManager.getSession();
		if (session!=null) {
			isRapidConn = true;
		}else {
			session = getSqlSession();
		}
		try {
			PreparedStatement pst = session.getConnection().prepareStatement(sql);
			Object result = null;
			SqlBuilder.fillStatement(pst, paras);
			doSqlLog(sql);
			doParLog(paras);
			ResultSet rs = pst.executeQuery();
			int colAmount = rs.getMetaData().getColumnCount();
			if (colAmount > 1) {
				List<Bean> list = BeanBuilder.build(rs);
                                result = list.size() > 0 ? list.get(0) : null;
			}
			else if(colAmount == 1) {
				if (rs.next()) {
					result = rs.getObject(1);
				}
			}
			closeRsAndStatement(rs, pst);
			return result;
		} catch (Exception e) {
			throw e;
		} finally{
			if (!isRapidConn&&session!=null) {
				session.close();
			}
		}
	}		
	
	/**
	 * 查询多条数据
	 * @param sql
	 * @param paras
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <T> List<T> queryList(String sql, Object... paras) throws Exception {
		boolean isRapidConn = false; //是否存在连接注入
		SqlSession session = MyBatisSessionManager.getSession();
		if (session!=null) {
			isRapidConn = true;
		}else {
			session = getSqlSession();
		}
		try {
			List result = new ArrayList();
			PreparedStatement pst = session.getConnection().prepareStatement(sql);
			SqlBuilder.fillStatement(pst, paras);
			doSqlLog(sql);
			doParLog(paras);
			ResultSet rs = pst.executeQuery();
			int colAmount = rs.getMetaData().getColumnCount();
			if (colAmount > 1) {
				result = BeanBuilder.build(rs);
			}
			else if(colAmount == 1) {
				while (rs.next()) {
					result.add(rs.getObject(1));
				}
			}
			closeRsAndStatement(rs, pst);
			return result;
		} catch (Exception e) {
			throw e;
		} finally{
			if (!isRapidConn&&session!=null) {
				session.close();
			}
		}
	}	
	
	//注入主键
	private static void getGeneratedKey(PreparedStatement pst, Bean bean, String[] pKeys) throws SQLException {
		ResultSet rs = pst.getGeneratedKeys();
		for (String pKey : pKeys)
			if (bean.get(pKey) == null)
				if (rs.next())
					bean.set(pKey, rs.getObject(1));
		rs.close();
	}
	
	//关闭statement
	private static void closeStatement(Statement st){
		if (st != null) {try {st.close();} catch (SQLException e) {}}
	}
	
	//关闭rs和statement
	private static void closeRsAndStatement(ResultSet rs, Statement st){
		if (rs != null) {try {rs.close();} catch (SQLException e) {}}
		if (st != null) {try {st.close();} catch (SQLException e) {}}
	}
	
	private static void doSqlLog(String sql){
		if (RapidConsts.isIS_DEV_MODE()) {
			log.warn("sql语句："+sql);
		}
	}
	
	private static void doParLog(Object[] messages){
		if (RapidConsts.isIS_DEV_MODE()) {
			StringBuilder msg = new StringBuilder("注入参数：");
			for(int i=0;i<messages.length;i++){
				msg.append(messages[i]);//将原来的toString()方法去掉，传入参数为null时，不会因为日志报错儿回滚失误; huangwei 2016.1.15
				if (i<messages.length-1) {
					msg.append(",");
				}
			}
			log.warn(msg.toString());
		}
	}
	
	private static void doParLog(List<Object> messages){
		if (RapidConsts.isIS_DEV_MODE()) {
			StringBuilder msg = new StringBuilder("注入参数：");
			for(int i=0;i<messages.size();i++){
				msg.append(messages.get(i));//将原来的toString()方法去掉，传入参数为null时，不会因为日志报错儿回滚失误; huangwei 2016.1.7
				if (i<messages.size()-1) {
					msg.append(",");
				}
			}
			log.warn(msg.toString());
		}
	}
}



